

class HmmSubject(object):
    def __init__(self, sArgSubject):
        self.aProbSt = [];
        self.aStates = [];
        self.aMaxSts = [];
        self.aTimes = [];
        self.sArgSubject = sArgSubject;
    
    def getStates(self, sTime, aActs_Time):
        aSt = [];
        #Get All States that belong to this Subject (in this time step "t")
        for actM in aActs_Time[sTime]:
            for sArg in actM.rootAct.aSubjects:
                if sArg == self.sArgSubject:
                    aSt.append(actM);
        self.aStates.append(aSt);
    
#     def getPastForward(self, sBin, aPastActNodes, aThisArgs):
# #        if sBin.count("1")==0:
# #            return 0.0;
#         nPastF = 1.0;
#         for i, sB in enumerate(sBin):
#             sArg = self.sArgSubject;
#             if not(aPastActNodes[i].mHmmProb.has_key(sArg)):
#                 for thisArg in aThisArgs:
#                     if aPastActNodes[i].mHmmProb.has_key(thisArg):
#                         sArg = thisArg;
#                         break;
#             if sB=="1":
#                 nPastF *= aPastActNodes[i].mHmmProb[sArg];
# #            elif sB=="0":
# #                nPastF *= aPastActNodes[i].mHmmNullProb[sArg];
#         return nPastF;
        
    
    def getProbSt(self, iT, graph):
        print "\nTime:", self.aTimes[iT], "- HMM:", self.sArgSubject;
        aProbSt_T = [];
        aProbSt_T.append(self.getNullSt(iT, graph));
        for actM in self.aStates[iT]: #Update Prob on each Value-Act (of this Time Step)
            node = graph.idndict[actM.sId];
            print "State:", actM.rootAct.name;
            nObsProb = 1;
            for i in range(len(self.aStates[iT])): #Likelihood of each Obs given this Value-Act
                if self.aStates[iT][i]==actM:
#                    print "  -Likelihood:", actM.nLikelihood;
                    nObsProb *= actM.nLikelihood;
                else:
#                    print "  -Null Likelihood:", 0.1;
                    nObsProb *= 0.01; #Change 0.1 to Null Prob Model: P(Obs|~M)
            nSumProb = 0;
            nLinks = len(graph.ndict[node][0]);
            if nLinks==0:
                print "  -Prior Prob:", graph.ndict[node][1][0];
                nSumProb += graph.ndict[node][1][0];
            elif nLinks>0:
                print "  -Transition Probs:", graph.ndict[node][1];
                for pastSt in graph.ndict[node][0]:
                    print "  -PastSt:", pastSt.name;
                nPastActs = len(graph.ndict[node][0]);
                for i in range(2**nPastActs):
                    sBin = bin(i)[2:].zfill(nPastActs);
                    print "  -Trans_Prob:", graph.ndict[node][1][i], "PastForward:", self.getPastForward(sBin, graph.ndict[node][0], node.aSubjects);
#                    nSumProb += graph.ndict[node][1][2**i] * self.aProbSt[iT-1][i];
#                    nSumProb += graph.ndict[node][1][2**i] * graph.ndict[node][0][i].nHmmProb;
                    nSumProb += graph.ndict[node][1][i] * self.getPastForward(sBin, graph.ndict[node][0], node.aSubjects);
            print "aProbSt_T: nObsProb * nSumProb = ", nObsProb, "*", nSumProb, "=", nObsProb * nSumProb;
            aProbSt_T.append(nObsProb * nSumProb);
        nZ = 1.0/sum(aProbSt_T); #Normalization Constant
        for i in range(len(aProbSt_T)): #Normalize Probs
            aProbSt_T[i] *= nZ;
        self.aProbSt.append(aProbSt_T);
        for i, actM in enumerate(self.aStates[iT],1): #Store these Probs into their Nodes
            actM.rootAct.mHmmProb[self.sArgSubject] = aProbSt_T[i];
            actM.rootAct.mHmmNullProb[self.sArgSubject] = aProbSt_T[0]; #Store Null Prob
        print "ProbSt[iT]:", self.aProbSt[iT];
        
        
    def getViterbi(self, iT, graph):
        print "\nTime:", self.aTimes[iT], "- HMM:", self.sArgSubject;
        aProbSt_T, nTransP = [], -1;
        for actM in self.aStates[iT]: #Get Prob of each path PastSt->[State] Activity (of this Time Step)
            nObsProb = actM.rootAct.aScores[0];
            nodeInfoCPT = graph.ndict[actM.rootAct];
            print "State:", actM.rootAct.name;
            nLinks = len(nodeInfoCPT[0]);
            if nLinks==0:
                nPrior = nodeInfoCPT[1][0];
                nTransP = nPrior;
                nPastP = 1;
#                 print "  -Prior Prob:", nPrior;
            elif nLinks>0:
#                 print "  -Transition Probs:", nodeInfoCPT[1];
#                 for pastSt in nodeInfoCPT[0]:
#                     print "  -PastSt:", pastSt.name;
#                 nPastActs = len(nodeInfoCPT[0]);
#                 if nPastActs==1: nPastActs = 2; #Considers both: [P(St|PastSt) & P(St|~PastSt)]
#                 for i in range(nPastActs):                    
#                     nProb = nodeInfoCPT[1][i] * self.aProbSt[iT-1];
#                     if nProb>=nMaxProb:
#                         nMaxProb = nProb;
                pastAct = self.aMaxSts[iT-1].rootAct;
                if pastAct in nodeInfoCPT[0]: 
                    iTrans = 2**nodeInfoCPT[0].index(pastAct);
                    nTransP = nodeInfoCPT[1][iTrans];
                else:
                    nTransP = 0;
                nPastP = self.aProbSt[iT-1];
            aProbSt_T.append(nObsProb * nTransP * nPastP);
#         nZ = 1.0/sum(aProbSt_T); #Normalization Constant
#         for i in range(len(aProbSt_T)): #Normalize Probs
#             aProbSt_T[i] *= nZ;
        nFinal_MaxProb = max(aProbSt_T);
        maxAct = self.aStates[iT][aProbSt_T.index(nFinal_MaxProb)]; 
        self.aProbSt.append(nFinal_MaxProb);
        self.aMaxSts.append(maxAct);
#         for i, actM in enumerate(self.aStates[iT],1): #Store these Probs into their Nodes
#             actM.rootAct.mHmmProb[self.sArgSubject] = aProbSt_T[i];

    def getMaxProbSeq(self, iT, graph):
        print "\nTime:", self.aTimes[iT], "- HMM:", self.sArgSubject;
        aStates = [];
        #Clamp last biggest Post
        if len(self.aMaxSts)>0:
            for actM in self.aStates[iT-1]:
                graph.addobs(actM.rootAct, False);
            graph.addobs(self.aMaxSts[-1].rootAct, True);
        #Get & compute all activities Posts
        for actM in self.aStates[iT]: #Get Prob of each path PastSt->[State] Activity (of this Time Step)
            aStates.append(actM.rootAct);
            print "     - States:", actM.rootAct.name;
        aPosts = graph.postprob(aStates);
        print aPosts;
        #Find biggest Post
        nFinal_MaxProb = max(aPosts);
        iMaxP = aPosts.index(nFinal_MaxProb);
        maxAct = self.aStates[iT][iMaxP]; 
        #Store biggest Post
        if len(self.aProbSt)==0:
            self.aProbSt.append(nFinal_MaxProb);
        else:
            self.aProbSt.append(self.aProbSt[-1]*nFinal_MaxProb);
        print self.aProbSt; 
        self.aMaxSts.append(maxAct);
        #Unclamp last Posts
        if len(self.aMaxSts)>0:
            for actM in self.aStates[iT-1]:
                graph.removeObs(actM.rootAct);
            
            
        
    
    def getMaxProbSt(self, iT, graph):        
        pass;
#        iAct = 0;
#        aProbSt_T = [];
#        for actM in self.aStates[iT]: #Update Prob on each Value-Act (of this Time Step)
#            node = graph.idndict[actM.sId];
#            nObsProb = 1;
#            for i in range(len(self.aStates[iT])): #Likelihood of each Obs given this Value-Act
#                if self.aStates[iT][i]==actM:
#                    nObsProb *= actM.nLikelihood;
#                else:
#                    nObsProb *= 0.1; #Change 0.1 to Null Prob Model: P(Obs|~M)
#            if iT==0:
#                nMaxSumProb = actM.nPrior; #Might be better to use the Marginalization                
#            elif iT>0:
#                aSumProb = [];
#                for i in range(len(graph.ndict[node][0])): #TEMPORARLY (later consider links that doesn't have sArgSubject)
#                    aSumProb.append(graph.ndict[node][1][2**i] * max(self.aProbSt[iT-1]));
#                nMaxSumProb = max(aSumProb);
#            aProbSt_T.append(nObsProb * nMaxSumProb);
#            iAct += 1;        
#        nZ = 1.0/sum(aProbSt_T); #Normalization Constant
#        for i in range(len(aProbSt_T)): #Normalize Probs
#            aProbSt_T[i] *= nZ;
#        self.aMaxSts.append(aProbSt_T.index(max(aProbSt_T)));
#        print "MaxSts[iT]:", self.aMaxSts;
    
    def updateHmm(self, sTime, aActs_Time, graph):
        self.aTimes.append(sTime);
        self.getStates(sTime, aActs_Time);
#         self.getViterbi(len(self.aTimes)-1, graph);
        self.getMaxProbSeq(len(self.aTimes)-1, graph);
#         self.getProbSt(len(self.aTimes)-1, graph);
#         self.getMaxProbSt(len(self.aTimes)-1, graph);
        